﻿package 
{
    import flash.display.*;
    import flash.events.*;
    import flash.text.*;
    import flash.utils.*;
    import flash.ui.Keyboard;
    import flash.geom.*;

    //import com.hexagonstar.util.debug.Debug;

    import mx.core.BitmapAsset;
    
    import org.papervision3d.cameras.*;
    import org.papervision3d.materials.*;
    import org.papervision3d.objects.*;
    import org.papervision3d.scenes.*;
    import org.papervision3d.core.proto.*;
    
    public class BaseDemo extends Sprite
    {
        protected var camera:Camera3D;
        protected var scene:Scene3D;
        protected var sceneroot:DisplayObject3D;
        private var callback:TraverseCallback;
        private var callbacks:Array = new Array();
        private var callbackindex:Number = 0;
        private var callbackdesc:Dictionary = new Dictionary();
        private var callbacktitle:Dictionary = new Dictionary();

        private var titlegroup:Sprite;
        private var statsgroup:Sprite;
        private var infogroup:Sprite;
        private var lefttopgroup:Sprite;

        private var copylabel:TextField;
        private var fpslabel:TextField;
        private var cpulabel:TextField;
        private var trilabel:TextField;
        private var messagelabel:TextField;
        private var titlelabel:TextField;

        private var nextbutton:Button;
        private var prevbutton:Button;

        private var dirty:Boolean = true;

        private var lastrender:int = 0;

        private var aparam:Number = 11.6;
        private var bparam:Number = -3;
        private var stageWidth:Number = 1;
        private var stageHeight:Number = 1;

        private var active:Boolean = false;

        private var demoroot:Sprite;

        public function BaseDemo(title:String)
        {
            stage.scaleMode = StageScaleMode.NO_SCALE;
            stage.showDefaultContextMenu = false;
            stage.stageFocusRect = false;
            this.x = 400;
            this.y = 300;

            demoroot = new Sprite();
            addChild(demoroot);
            
            scene = new Scene3D(demoroot);

            lefttopgroup = new Sprite();
            addChild(lefttopgroup);

            statsgroup = new Sprite();
            lefttopgroup.addChild(statsgroup);

            statsgroup.addChild(new Panel(5, 5, 400, 70, 0.8));

            fpslabel = new TextField();
            fpslabel.x = 10;
            fpslabel.y = 10;
            fpslabel.defaultTextFormat = new TextFormat("Arial", 16, 0x000000);
            fpslabel.text = "";
            fpslabel.background = true;
            fpslabel.height = 20;
            fpslabel.width = 200;
            fpslabel.backgroundColor = 0xCC0000;
            statsgroup.addChild(fpslabel);

            cpulabel = new TextField();
            cpulabel.x = fpslabel.x;
            cpulabel.y = fpslabel.y + fpslabel.height;
            cpulabel.defaultTextFormat = fpslabel.defaultTextFormat;
            cpulabel.text = "";
            cpulabel.background = true;
            cpulabel.height = 20;
            cpulabel.width = 200;
            cpulabel.backgroundColor = 0x0000CC;
            statsgroup.addChild(cpulabel);
    
            trilabel = new TextField();
            trilabel.x = cpulabel.x;
            trilabel.y = cpulabel.y + cpulabel.height;
            trilabel.defaultTextFormat = fpslabel.defaultTextFormat;
            trilabel.text = "";
            trilabel.height = 22;
            trilabel.width = 200;
            statsgroup.addChild(trilabel);

            infogroup = new Sprite();
            lefttopgroup.addChild(infogroup);
            infogroup.y = statsgroup.height + 4;

            infogroup.addChild(new Panel(5, 5, 250, 410, 0.8));

            messagelabel = new TextField();
            messagelabel.autoSize = TextFieldAutoSize.LEFT;
            messagelabel.wordWrap = true;
            messagelabel.x = 10;
            messagelabel.y = 10;
            messagelabel.defaultTextFormat = trilabel.defaultTextFormat;
            messagelabel.text = "";
            messagelabel.width = 240;
            messagelabel.multiline = true;
            infogroup.addChild(messagelabel);

            prevbutton = new Button("Prev", 46);
            prevbutton.x = 80;
            prevbutton.y = infogroup.height - 52;
            infogroup.addChild(prevbutton);

            nextbutton = new Button("Next", 46);
            nextbutton.x = prevbutton.x + prevbutton.width + 4;
            nextbutton.y = prevbutton.y;
            infogroup.addChild(nextbutton);

            copylabel = new TextField();
            copylabel.autoSize = TextFieldAutoSize.CENTER;
            copylabel.x = 124;
            copylabel.y = infogroup.height - 30;
            copylabel.defaultTextFormat = new TextFormat("Arial", 10, 0x000000, true);
            copylabel.htmlText = "<p align='center'>Alexander Zadorozhny (c) 2007<br>\n<a href='http://away.kiev.ua/flash'>http://away.kiev.ua/flash</a></p>";
            infogroup.addChild(copylabel);

            titlegroup = new Sprite();
            lefttopgroup.addChild(titlegroup);
            titlegroup.x = statsgroup.width + 4;

            titlegroup.addChild(new Panel(5, 5, 360, 36, 0.8));
			
            titlelabel = new TextField();
            titlelabel.autoSize = TextFieldAutoSize.CENTER;
            titlelabel.x = 180;
            titlelabel.y = 10;
            titlelabel.defaultTextFormat = new TextFormat("Arial", 18, 0x000000, true);
            titlelabel.htmlText = title;
            titlegroup.addChild(titlelabel);

            // Create camera
            camera = new Camera3D();
            camera.x = 3000;
            camera.z = -300;
            camera.zoom = 10;
            camera.focus = 100;
            camera.sort = false;
    
            sceneroot = scene.addChild(new DisplayObject3D("sceneroot"));

            stage.addEventListener(Event.ENTER_FRAME, onEnterFrame);
            stage.addEventListener(Event.RESIZE, onResize);
            stage.addEventListener(Event.ACTIVATE, onActivate);
            stage.addEventListener(Event.DEACTIVATE, onDeactivate);
            stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
            stage.addEventListener(MouseEvent.MOUSE_WHEEL, onMouseWheel);
            prevbutton.addEventListener(MouseEvent.CLICK, onPrev);
            nextbutton.addEventListener(MouseEvent.CLICK, onNext);

            this.onResize(null);
        }

        protected function addCallback(title:String, text:String, callback:TraverseCallback):void
        {
            callbacks.push(callback);
            callbackdesc[callback] = text;
            callbacktitle[callback] = title;
        }

        private function changeCallback():void
        {
            callback = callbacks[callbackindex];
            dirty = true;
            messagelabel.htmlText = "<font size='16' face='arial'>"
                            +"<p align='center'><b>"+callbacktitle[callback]+"</b></p>"
                            +"<p align='left'>"+callbackdesc[callback]+"</p>"
                            +"</font>";
            nextbutton.visible = callbackindex < callbacks.length-1;
            prevbutton.visible = callbackindex > 0;
        }

        private function onNext(event:MouseEvent):void
        {
            callbackindex = Math.min(callbacks.length-1, callbackindex+1);
            changeCallback();
        }

        private function onPrev(event:MouseEvent):void
        {
            callbackindex = Math.max(0, callbackindex-1);
            changeCallback();
        }

        protected static function mat(image:Object):BitmapMaterial 
        {
            return new BitmapMaterial((image as BitmapAsset).bitmapData);
        }

        private function onEnterFrame(event:Event):void
        {
            if (callback == null)
                changeCallback();

            if (!active)
                return;

            dirty = true;   

            if (this.mouseX > stageWidth*0.40)
                aparam -= 0.1;
            if (this.mouseX < -stageWidth*0.40)
                aparam += 0.1;

            if (this.mouseY > stageHeight*0.40)
                bparam += 0.1;
            if (this.mouseY < -stageHeight*0.40)
                bparam -= 0.1;

            if (bparam > -0.1)
                bparam = -0.1;

            if (bparam < -24.3)
                bparam = -24.3;

            if (camera.hover(0, aparam, bparam, 5))
                dirty = true;
    
            if (!dirty)
            {
                fpslabel.width = 0;
                cpulabel.width = 0;
                return;
            }

            var start:int = getTimer();

            scene.renderCamera(camera, callback);
            
            var now:int = getTimer();
            var performance:int = now - lastrender;
            lastrender = now;

            if (performance < 1000)
            {
                fpslabel.text = "" + int(1000 / (performance+0.001)) + " fps " + performance + " ms";
                fpslabel.width = 4 * performance;
            }

            var think:int = now - start;
            if (think < 1000)
            {
                cpulabel.width = 4 * think;
            }

            trilabel.text = scene.stats.rendered + " triangles";

            dirty = false;
        }

        private function onResize(event:Event):void 
        {
            stageWidth = stage.stageWidth;
            stageHeight = stage.stageHeight;

            lefttopgroup.x = - stageWidth / 2;
            lefttopgroup.y = - stageHeight / 2;

            scene.clip = new Rectangle(- stageWidth / 2 - 20, - stageHeight / 2 - 20, stageWidth + 40, stageHeight + 40);

            //Debug.trace("stageWidth: " + stage.stageWidth + " stageHeight: " + stage.stageHeight);
        }

        private function onActivate(event:Event):void 
        {
            //Debug.trace("activate");
            onResize(null);
            active = true;
        }

        private function onDeactivate(event:Event):void 
        {
            //Debug.trace("deactivate");
            active = false;
            fpslabel.width = 0;
            cpulabel.width = 0;
        }

        private function onKeyDown(event:KeyboardEvent):void
        {
            switch (event.keyCode)
            {
                case Keyboard.SPACE:
                    onNext(null);
                    break;
                case Keyboard.BACKSPACE:
                    onPrev(null);
                    break;
                case Keyboard.CONTROL:
                    lefttopgroup.visible = !lefttopgroup.visible;
                    break;
                case Keyboard.SHIFT:
                    //Debug.trace(scene.findNearest(camera, demoroot.mouseX, demoroot.mouseY).tri.screenZ);
                    break;
                case Keyboard.NUMPAD_ADD:
                    adjustzoom(3);
                    break;
                case Keyboard.NUMPAD_SUBTRACT:
                    adjustzoom(-3);
                    break;
            }
        }

        private function onMouseWheel(event:MouseEvent):void
        {
            adjustzoom(event.delta)
        }

        private function adjustzoom(delta:int):void
        {
            camera.zoom = Math.min(21, Math.max(2, camera.zoom + delta / 3));
        }
    }
}
